#include <middleware/gm_api/skfapi.h>
#include <middleware/gm_api/skfapi_ex.h>
#include <middleware/mk_lib/mk_auto_mutex.h>
#include <middleware/mk_lib/mk_utility.h>
#include <middleware/mk_lib/mk_logger.h>
#include <middleware/apdu/apdu.lib/apdu_lib_cryption_mgr.h>
#include <middleware/apdu/apdu.lib/apdu_lib_dev_config.h>
#include <middleware/apdu/apdu.algo/pkcs5_padding.h>

#include <stdio.h>

#include "gm_global.h"
#include "gm_defs.h"
#include "../gm_data_mgr/gm_sc_mgr.h"

ULONG DEVAPI SKF_EncryptInitHS (HANDLE hKey, BLOCKCIPHERPARAM EncryptParam)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_EncryptInitHS()");

	ULONG			lRslt = SAR_OK;
	int maxTrans = 0;
	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	if(pkey->check_iv_len(EncryptParam.IVLen) != SAR_OK)
	{
		lRslt = SAR_INVALIDPARAMERR;
		ERROR_THROW(lRslt);
	}

	pkey->set_iv(EncryptParam.IV, EncryptParam.IVLen);
	pkey->set_padding_type(EncryptParam.PaddingType);
	pkey->set_first_package(TRUE);

	maxTrans = pDev->get_max_apdu_len();
	pkey->init_mem_stream(maxTrans * 2);

END_OF_FUN:
	DBG_LOGMSG("leave SKF_EncryptInitHS(), ret=%08x", lRslt);
	return lRslt;
}

ULONG DEVAPI SKF_DecryptInitHS(HANDLE hKey, BLOCKCIPHERPARAM DecryptParam)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_DecryptInitHS()");

	ULONG			lRslt = SAR_OK;
	int maxTrans = 0;
	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	pkey->set_iv(DecryptParam.IV, DecryptParam.IVLen);
	pkey->set_padding_type(DecryptParam.PaddingType);
	pkey->set_first_package(TRUE);

	maxTrans = pDev->get_max_apdu_len();
	pkey->init_mem_stream(maxTrans * 2);

END_OF_FUN:
	DBG_LOGMSG("leave SKF_DecryptInitHS(), ret=%08x", lRslt);
	return lRslt;
}


ULONG DEVAPI SKF_EncryptUpdateHS(HANDLE hKey, BYTE * pbData, ULONG ulDataLen, 
								 BYTE *pbEncryptedData, ULONG *pulEncryptedLen)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_EncryptUpdateHS(), ulDataLen=%ld", ulDataLen);

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			ulEncryptedTempdatalen = 0;
	ULONG			ulEncrypted_len = 0;
	BYTE			*pEncrytedTempdata = NULL;

	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;
	gm_stream_mgr * pStreamMgr = NULL;
	int				_lastlen = 0;
	BYTE			*header = NULL;
	int				block_size = 0;
	int				expSize = 0;
	int maxTransLen;
	BYTE	*encrypted_ptr;

	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	pStreamMgr = pkey->get_stream_mgr();
	_lastlen = pStreamMgr->get_data_len();
	block_size = pkey->get_block_size();

	expSize = pkey->get_block_align_result_len(ulDataLen + _lastlen);

	if(pbEncryptedData == NULL)
	{
		*pulEncryptedLen = expSize;
		goto END_OF_FUN;
	}

	maxTransLen = pDev->get_max_hs_data_len();

	ulEncrypted_len = expSize + maxTransLen;
	pEncrytedTempdata = new BYTE[ulEncrypted_len];
	memset(pEncrytedTempdata, 0, ulEncrypted_len);

	encrypted_ptr = pEncrytedTempdata;

	pStreamMgr->set_max_apdu_len(maxTransLen);

	TRACE_ASSERT_LEN(*pulEncryptedLen, expSize);
	
	header = pStreamMgr->push_data(pbData, ulDataLen);

	_lastlen = pStreamMgr->get_data_len();

	iv = pkey->get_iv(&iv_len);
	while(_lastlen = pkey->get_encrypt_data_len())
	{
		ulEncrypted_len = maxTransLen;
		lRslt = app_encrypt_update_hs(hdev, pkey->Algid(), app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, _lastlen, encrypted_ptr, (int *)&ulEncrypted_len);
		ERROR_THROW_APDU(lRslt)

		ulEncryptedTempdatalen += ulEncrypted_len;
		encrypted_ptr = encrypted_ptr + ulEncrypted_len;

		pStreamMgr->pop_data(_lastlen);
		pkey->set_first_package(FALSE);
	}

	TRACE_ASSERT_LEN(*pulEncryptedLen, ulEncryptedTempdatalen)

	memcpy(pbEncryptedData, pEncrytedTempdata, ulEncryptedTempdatalen);
	*pulEncryptedLen = ulEncryptedTempdatalen;

END_OF_FUN:

	DELETE_BUFFER_PTR(pEncrytedTempdata)
	DBG_LOGMSG("leave SKF_EncryptUpdateHS(), ret=%08x", lRslt);
	
	return lRslt;

}

ULONG DEVAPI SKF_EncryptFinalHS (HANDLE hKey, BYTE *pbEncryptedData, ULONG *ulEncryptedDataLen )
{
	MK_AUTO_MUTEX
		DBG_LOGMSG("enter SKF_EncryptFinalHS()");

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			ulEncrypted_len = get_max_transmit_len();
	BYTE			*pEncrytedTempdata = new BYTE[ulEncrypted_len];
	BYTE			*encrypted_ptr = pEncrytedTempdata;
	BYTE			pFinalData[32] = {0};
	ULONG			ulEncrytedTempdataLen = 0;
	int				data_len = 0;
	int				_lastlen =0;
	BYTE			*header = 0;
	int				expSize = 0;
	unsigned char   padlen = 0;
	int				i = 0;

	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;
	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	//only works for high speed device
	app_dev_enable_high_speed(hdev, 0);
	//

	_lastlen = pkey->get_stream_mgr()->get_data_len();
	header = pkey->get_stream_mgr()->get_data_ptr();

	expSize = pkey->get_padding_result_len(_lastlen);
	if(pbEncryptedData == NULL)
	{
		*ulEncryptedDataLen = expSize;
		goto END_OF_FUN;
	}

	if(SAR_OK != pkey->check_alg_data_len(_lastlen))
	{
		lRslt = SAR_INVALIDPARAMERR;
		goto END_OF_FUN;
	}

	//padding
	if(pkey->get_padding_type() == 1)
	{
		padlen = pkcs5_get_padlen(pkey->get_block_size(), _lastlen);
		for(i = 0; i < padlen; i ++)
		{
			pkey->get_stream_mgr()->push_data(&padlen,1);
		}
	}

	TRACE_ASSERT_LEN(*ulEncryptedDataLen, expSize)
	memset(pEncrytedTempdata, 0, sizeof(pEncrytedTempdata));

	data_len = pkey->get_stream_mgr()->get_data_len();

	if(data_len > 0)
	{
		lRslt = app_encrypt_update_hs(hdev, pkey->Algid(), app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, data_len, encrypted_ptr, (int *)&ulEncrypted_len);
		ERROR_THROW_APDU(lRslt)

		encrypted_ptr += ulEncrypted_len;
		ulEncrytedTempdataLen += ulEncrypted_len;

		pkey->get_stream_mgr()->pop_data(data_len);
	}
	TRACE_ASSERT_LEN(*ulEncryptedDataLen, ulEncrytedTempdataLen)

	memcpy(pbEncryptedData, pEncrytedTempdata, ulEncrytedTempdataLen);
	*ulEncryptedDataLen = ulEncrytedTempdataLen;

	pkey->get_stream_mgr()->release();

END_OF_FUN:
	DELETE_BUFFER_PTR(pEncrytedTempdata)
		DBG_LOGMSG("leave SKF_EncryptFinalHS(), ret=%08x", lRslt);
	return lRslt;

}



ULONG DEVAPI SKF_DecryptUpdateHS(HANDLE hKey, BYTE * pbEncryptedData, ULONG ulEncryptedLen, 
								 BYTE * pbData, ULONG * pulDataLen)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_DecryptUpdateHS(), ulEncryptedLen=%ld", ulEncryptedLen);

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			ulDecryptedTempdatalen = 0;
	ULONG			ulDecrypted_len = 0;
	BYTE			*pDecrytedTempdata = NULL;
	int				_lastlen = 0;
	BYTE			*header = NULL;

	BYTE	*pDecryted_ptr = NULL;
	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;
	gm_stream_mgr * pStreamMgr = NULL;
	int maxTransLen;
	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	maxTransLen = pDev->get_max_hs_data_len();

	pStreamMgr = pkey->get_stream_mgr();
	pStreamMgr->set_max_apdu_len(maxTransLen);

	_lastlen = pStreamMgr->get_data_len();

	if(pbData == NULL)
	{
		*pulDataLen =  pkey->get_block_align_result_len(_lastlen + ulEncryptedLen);
		goto END_OF_FUN;
	}

	ulDecrypted_len = _lastlen + ulEncryptedLen + maxTransLen;
	pDecrytedTempdata = new BYTE[ulDecrypted_len];
	memset(pDecrytedTempdata, 0, ulDecrypted_len);

	pDecryted_ptr = pDecrytedTempdata;

	header = pStreamMgr->push_data(pbEncryptedData, ulEncryptedLen);

	app_dev_enable_high_speed(hdev, 1);

	iv = pkey->get_iv(&iv_len);

	while(_lastlen = pkey->get_decrypt_data_len())
	{
		ulDecrypted_len = _lastlen;
		lRslt = app_decrypt_update_hs(hdev, pkey->Algid(), app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, _lastlen, pDecryted_ptr, (int *)&ulDecrypted_len);
		ERROR_THROW_APDU(lRslt);

		ulDecryptedTempdatalen += ulDecrypted_len;
		pDecryted_ptr = pDecryted_ptr + ulDecrypted_len;
		pStreamMgr->pop_data(_lastlen);
		pkey->set_first_package(FALSE);
	}

	TRACE_ASSERT_LEN(*pulDataLen, ulDecryptedTempdatalen);

	memcpy(pbData, pDecrytedTempdata, ulDecryptedTempdatalen);
	*pulDataLen = ulDecryptedTempdatalen;

END_OF_FUN:
	app_dev_enable_high_speed(hdev, 0);

	DELETE_BUFFER_PTR(pDecrytedTempdata)
		DBG_LOGMSG("leave SKF_DecryptUpdateHS(), ret=%08x", lRslt);
	return lRslt;
}


ULONG DEVAPI SKF_EncryptHS(HANDLE hKey, BYTE * pbData, ULONG ulDataLen, 
						 BYTE *pbEncryptedData, ULONG *pulEncryptedLen)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_EncryptHS(), ulDataLen=%ld", ulDataLen);

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			ulEncryptedTempdatalen = 0;
	ULONG			ulEncrypted_len = 0;
	BYTE			*pEncrytedTempdata = NULL;

	BYTE			*header = NULL;
	BYTE			pFinalData[32] = {0};
	int				_lastlen = 0;
	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;
	gm_stream_mgr *  pStreamMgr = NULL;
	BYTE		*encrypted_ptr = NULL;
	int expSize = 0;
	int maxTransLen = 0;
	unsigned char padlen = 0;

	int i = 0;
	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	pStreamMgr = pkey->get_stream_mgr();

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	if(pbEncryptedData == NULL)
	{
		*pulEncryptedLen = pkey->get_padding_result_len(ulDataLen);
		goto END_OF_FUN;
	}

	maxTransLen = pDev->get_max_hs_data_len();
	pStreamMgr->set_max_apdu_len(maxTransLen);

	expSize = pkey->get_padding_result_len(ulDataLen);
	TRACE_ASSERT_LEN(*pulEncryptedLen,expSize);

	header = pStreamMgr->push_data(pbData, ulDataLen);

	//padding
	if(pkey->get_padding_type() == 1)
	{
		padlen = pkcs5_get_padlen(pkey->get_block_size(), pStreamMgr->get_data_len());
		for(i = 0; i < padlen; i ++)
		{
			pStreamMgr->push_data(&padlen,1);
		}
	}

	iv = pkey->get_iv(&iv_len);

	ulEncrypted_len = ulDataLen + maxTransLen;
	pEncrytedTempdata = new BYTE[ulEncrypted_len];
	memset(pEncrytedTempdata, 0, ulEncrypted_len);

	encrypted_ptr = pEncrytedTempdata;
	while(_lastlen = pkey->get_encrypt_data_len())
	{
		ulEncrypted_len = maxTransLen;
		lRslt = app_encrypt_update_hs(hdev, pkey->Algid(), app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, _lastlen, encrypted_ptr, (int *)&ulEncrypted_len);
		ERROR_THROW_APDU(lRslt) 

		ulEncryptedTempdatalen += ulEncrypted_len;
		encrypted_ptr += ulEncrypted_len;

		pStreamMgr->pop_data(_lastlen);
		pkey->set_first_package(FALSE);
	}

	TRACE_ASSERT_LEN(*pulEncryptedLen, ulEncryptedTempdatalen)

	memcpy(pbEncryptedData, pEncrytedTempdata, ulEncryptedTempdatalen);
	*pulEncryptedLen = ulEncryptedTempdatalen;

	pStreamMgr->release();

END_OF_FUN:
	DELETE_BUFFER_PTR(pEncrytedTempdata)
	DBG_LOGMSG("leave SKF_EncryptHS(), ret=%08x", lRslt);

	return lRslt;
}


ULONG DEVAPI SKF_DecryptHS(HANDLE hKey, BYTE *pbEncryptedData, ULONG ulEncryptedLen, BYTE * pbData, ULONG * pulDataLen)
{
	MK_AUTO_MUTEX
	DBG_LOGMSG("enter SKF_DecryptHS(), ulEncryptedLen=%ld", ulEncryptedLen);

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			ulDecryptedTempdatalen = 0;
	ULONG			ulDecrypted_len = ulEncryptedLen;
	BYTE			*pDecrytedTempdata = new BYTE[ulEncryptedLen];
	BYTE			*pDecryted_ptr = pDecrytedTempdata;
	BYTE			pFinalData[32] = {0};
	int				_lastlen = 0;
	BYTE			*header = NULL;
	int maxTransLen = 0;
	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;
	gm_stream_mgr *  pStreamMgr = NULL;

	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(pbData == NULL)
	{
		*pulDataLen = ulEncryptedLen;
		goto END_OF_FUN;
	}

	TRACE_ASSERT_LEN(*pulDataLen, ulEncryptedLen);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	pStreamMgr = pkey->get_stream_mgr();

	_lastlen = pStreamMgr->get_data_len();

	header = pStreamMgr->push_data(pbEncryptedData, ulEncryptedLen);
	memset(pDecrytedTempdata, 0, ulEncryptedLen);
	
	maxTransLen = pDev->get_max_hs_data_len();
	pStreamMgr->set_max_apdu_len(maxTransLen);

	iv = pkey->get_iv(&iv_len);

	while(_lastlen = pStreamMgr->get_block_data_len())
	{
		ulDecrypted_len = ulEncryptedLen;
		lRslt = app_decrypt_update_hs(hdev, pkey->Algid(),  app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, _lastlen, pDecryted_ptr, (int *)&ulDecrypted_len);
		ERROR_THROW_APDU(lRslt);

		ulDecryptedTempdatalen += ulDecrypted_len;
		pDecryted_ptr += ulDecrypted_len;
		pStreamMgr->pop_data(_lastlen);

		pkey->set_first_package(FALSE);
	}
	
	//de-padding
	if(pkey->get_padding_type() == 1)
	{
		pkcs5_unpadding(pkey->get_block_size(), pDecrytedTempdata, ulDecryptedTempdatalen, (long*)&ulDecryptedTempdatalen);
	}

	pStreamMgr->release();

	TRACE_ASSERT_LEN(*pulDataLen, ulDecryptedTempdatalen)

	memcpy(pbData, pDecrytedTempdata, ulDecryptedTempdatalen);
	*pulDataLen = ulDecryptedTempdatalen;

END_OF_FUN:
	DELETE_BUFFER_PTR(pDecrytedTempdata);
	DBG_LOGMSG("leave SKF_DecryptHS(), ret=%08x", lRslt);
	return lRslt;
}


ULONG DEVAPI SKF_DecryptFinalHS (HANDLE hKey, BYTE *pbPlainText, ULONG *pulPlainTextLen)
{
	MK_AUTO_MUTEX
		DBG_LOGMSG("enter SKF_DecryptFinalHS(), pulPlainTextLen=%ld", pulPlainTextLen);

	ULONG			lRslt = SAR_OK;
	HANDLE			hdev;
	ULONG			app_id, cont_id;
	ULONG			output_len = 0;
	ULONG			ulDecrypted_len = get_max_transmit_len();
	BYTE			*pDecrytedTempdata = new BYTE[ulDecrypted_len];
	BYTE			*pDecryted_ptr = pDecrytedTempdata;
	int				data_len = 256;
	int				_lastlen = 0;
	BYTE			*header = NULL;
	
	int iv_len = 0;
	unsigned char * iv = NULL;

	gm_sc_dev * pDev = NULL;
	gm_sc_app * pApp = NULL;
	gm_sc_cont * pCont = NULL;

	gm_sc_key		*pkey = gm_sc_mgr::get_dev_ptr()->find_key(hKey, pDev, pApp, pCont);
	if(!pkey)
	{
		lRslt = SAR_INVALIDHANDLEERR;
		ERROR_THROW(lRslt);
	}

	app_id = pApp->id();
	cont_id = pCont->id();
	hdev = pDev->get_apdu_handle();

	_lastlen = pkey->get_stream_mgr()->get_data_len();
	header = pkey->get_stream_mgr()->get_data_ptr();

	if(pbPlainText == NULL)
	{
		*pulPlainTextLen = _lastlen;
		goto END_OF_FUN;
	}

	if(_lastlen % pkey->get_block_size())
	{
		lRslt = SAR_INDATALENERR;
		goto END_OF_FUN;
	}

	memset(pDecrytedTempdata, 0, sizeof(pDecrytedTempdata));

	iv = pkey->get_iv(&iv_len);

	if(_lastlen)
	{
		lRslt = app_decrypt_update_hs(hdev, pkey->Algid(),  app_id, cont_id, pkey->key_id(),
			pkey->get_first_package(), iv, iv_len,
			header, _lastlen, pDecryted_ptr, (int *)&ulDecrypted_len);
		ERROR_THROW_APDU(lRslt);

		output_len += ulDecrypted_len;
		pDecryted_ptr += ulDecrypted_len;
		pkey->get_stream_mgr()->pop_data(data_len);
	}
	
	//de-padding
	if(pkey->get_padding_type() == 1 && output_len > 0)
	{
		pkcs5_unpadding(pkey->get_block_size(), pDecrytedTempdata, output_len, (long*)&output_len);
	}

	TRACE_ASSERT_LEN(*pulPlainTextLen, output_len)

	memcpy(pbPlainText, pDecrytedTempdata, output_len);
	*pulPlainTextLen = output_len;
	pkey->get_stream_mgr()->release();
END_OF_FUN:
	DELETE_BUFFER_PTR(pDecrytedTempdata)
		DBG_LOGMSG("leave SKF_DecryptFinalHS(), ret=%08x", lRslt);
	return lRslt;
}
